package src.ee.ioc.smaug.parser;

import java.io.*;
import javax.xml.parsers.*;
import org.xml.sax.*;
import org.xml.sax.helpers.*;

import src.ee.ioc.smaug.fsm.*;
import src.ee.ioc.smaug.world.evolution.*;


public class FSMParser extends DefaultHandler {

    protected FiniteStateMachine fsm;

    protected Alphabet alpha = new Alphabet();

    protected Alphabet beta = new Alphabet();

    protected State onestate;

    protected Transition onetransition;

    public static final EntityResolver ENTITY_RESOLVER = new EntityResolver() {

        public InputSource resolveEntity(String publicId, String systemId) {
            if (systemId != null && systemId.endsWith("dtd")) {
                return new InputSource(EnvDescription.fsmdtd);
            }
            return null;
        }

    };

    public static FiniteStateMachine createFSM(File f) {
        return new FSMParser(f).getFSM();

    }

    private FiniteStateMachine getFSM() {
        return fsm;
    }

    private class FSMHandler extends DefaultHandler {
        /*
         * Constants for package file format tokens
         */

        private static final String EL_FSM = "fsm";

        private static final String ATR_FSM_TYPE = "mtype";

        private static final String VAL_MOORE = "moore";

        private static final String ATR_FSM_NUMBEROFSTATES = "numberofstates";

        private static final String ATR_FSM_DESCRIPTION = "description";

        private static final String EL_ALPHABET = "alphabet";

        private static final String ATR_ALPHABET_TYPE = "atype";

        private static final String VAL_OUTPUT = "output";

        private static final String VAL_INPUT = "input";

        private static final String EL_CHAR = "char";

        private static final String ATR_CHAR_TYPE = "ctype";

        private static final String ATR_CHAR_VALUE = "cvalue";

        private static final String EL_STATES = "states";

        private static final String EL_STATE = "state";

        private static final String ATR_STATE_INITIAL = "initial";

        private static final String ATR_STATE_FINAL = "final";

        private static final String ATR_STATE_VALUE = "svalue";

        private static final String ATR_STATE_ID = "id";

        private static final String EL_TRANSITIONS = "transitions";

        private static final String EL_TRANSITION = "transition";

        private static final String ATR_TRANSITION_FROM = "from";

        private static final String ATR_TRANSITION_TO = "to";

        private static final String ATR_TRANSITION_READING = "reading";

        private static final String ATR_TRANSITION_WRITING = "writing";

        private StringBuilder charBuf;

        private String element;

        FSMHandler() {
            super();
        }

        @Override
        public void startDocument() {
            if (charBuf == null)
                charBuf = new StringBuilder();
        }

        @Override
        public InputSource resolveEntity(String publicId, String systemId)
                throws IOException, SAXException {

            return ENTITY_RESOLVER.resolveEntity(publicId, systemId);
        }

        @Override
        public void error(SAXParseException spe) {
            // Error generated by the parser
        }

        @Override
        public void warning(SAXParseException e) {
        }

        @Override
        public void setDocumentLocator(Locator l) { // Save this to resolve
            // relative URIs or to give
            // diagnostics.
        }

        @Override
        public void startElement(String namespaceURI, String lName,
                String qName, Attributes attrs) {
            element = qName;
            if (element.equals(EL_FSM)) {
                String mtype = attrs.getValue(ATR_FSM_TYPE);
                if (mtype != null && mtype.equals(VAL_MOORE)) {
                    fsm = new MooreMachine();
                }
            } else if (element.equals(EL_ALPHABET)) {
                // String atype = attrs.getValue(ATR_ALPHABET_TYPE);

                /*
                 * if (atype != null && atype.equals(VAL_INPUT)) { alpha = new
                 * Alphabet(); } else if (atype != null &&
                 * atype.equals(VAL_OUTPUT)) { beta = new Alphabet(); }
                 */
            } else if (element.equals(EL_CHAR)) {
                String ctype = attrs.getValue(ATR_CHAR_TYPE);
                String cvalue = attrs.getValue(ATR_CHAR_VALUE);
                if (ctype != null && ctype.equals(VAL_INPUT)) {
                    cvalue = attrs.getValue(ATR_CHAR_VALUE);
                    alpha.addToAlphabet(cvalue.charAt(0));
                } else if (ctype != null && ctype.equals(VAL_OUTPUT)) {
                    cvalue = attrs.getValue(ATR_CHAR_VALUE);
                    beta.addToAlphabet(cvalue.charAt(0));
                }
            } else if (element.equals(EL_STATE)) {
                String sinitial = attrs.getValue(ATR_STATE_INITIAL);
                String sfinal = attrs.getValue(ATR_STATE_FINAL);
                String svalue = attrs.getValue(ATR_STATE_VALUE);
                String sid = attrs.getValue(ATR_STATE_ID);
               
                    onestate = new State(Integer.parseInt(sid), svalue
                            .charAt(0), Boolean.parseBoolean(sinitial), Boolean
                            .parseBoolean(sfinal));
              
               
            } else if (element.equals(EL_TRANSITION)) {
                String tfrom = attrs.getValue(ATR_TRANSITION_FROM);
                String tto = attrs.getValue(ATR_TRANSITION_TO);
                String treading = attrs.getValue(ATR_TRANSITION_READING);
                String twriting = attrs.getValue(ATR_TRANSITION_WRITING);
                State fromState = fsm.getState(Integer.parseInt(tfrom));
                State toState = fsm.getState(Integer.parseInt(tto));
              
                
                    onetransition = new Transition(fromState, toState, treading
                            .charAt(0), twriting.charAt(0));
                
            }

        }

        @Override
        public void endElement(String namespaceURI, String sName, String qName) {
             if (qName.equals(EL_ALPHABET)) {
                fsm.setInputAlphabet(alpha);
                fsm.setOutputAlphabet(beta);
             
            } else if (qName.equals(EL_STATES)) {
                fsm.setBounds();

            } else if (qName.equals(EL_STATE)) {
                fsm.addState(onestate);
            } else if (qName.equals(EL_TRANSITION)) {
                fsm.addTransition(onetransition);
            }
        }

        @Override
        public void characters(char[] buf, int offset, int len) {
            // Characters may be delivered in multiple chunks so we just
            // accumulate them here.
            charBuf.append(buf, offset, len);
        }

    }

    private FSMParser(File file) {

        // Use an instance of ourselves as the SAX event handler
        DefaultHandler handler = new FSMHandler();

        // Use the validating parser
        SAXParserFactory factory = SAXParserFactory.newInstance();
        // factory.setFeature("http://xml.org/sax/features/validation", true);
        factory.setValidating(true);

        try {
            // Parse the input
            SAXParser saxParser = factory.newSAXParser();
            saxParser.parse(file, handler);
            // factory.setFeature("http://xml.org/sax/features/validation",
            // true);
            factory.setValidating(true);
        } catch (SAXException sxe) {
            // Error generated by this application (or a parser-initialization
            // error)
            System.err
                    .println("Error generated by this application (or a parser-initialization error)");
        } catch (ParserConfigurationException pce) {
            // Parser with specified options can't be built
            System.err.println("Parser with specified options can't be built");

        } catch (IOException ioe) {
            // I/O error
            System.err.println("I/O error");
        }

    }


}
